None
**Описание проекта:**
Интернет-магазин товаров для дома «Пока все ещё тут» в срочном порядке ищет аналитиков. Надо помочь магазину стать лучше, а клиентам — обустроить дом своей мечты. «Пока все ещё тут» — мы создаём уют!
**Заказчик отчета:** Менеджер по продукту/категорийный менеджер
**Цель:** выяснить «ненужный» товарный ряд для выведения их из продуктовой линейки с помощью выявления основого и дополнительного товаров. Оптимизация товарного ассортимента.
**Задачи:**
**Описание данных**
Колонки в ecommerce_dataset.csv :
date — дата заказа;customer_id — идентификатор покупателя;order_id — идентификатор заказа;product — наименование товара;quantity — количество товара в заказе;price — цена товара.**Оглавление**
**Выполнение проекта**
# Загружаем библиотеки
import pandas as pd
import numpy as np
import datetime as dt
from matplotlib.pyplot import figure
import warnings
warnings.filterwarnings('ignore')
import matplotlib.pyplot as plt
import matplotlib as mpl
import matplotlib.pylab as pylab
import seaborn as sns
from matplotlib.ticker import FuncFormatter
from plotly import graph_objects as go
import plotly.express as px
from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot
import folium
# Путь к внешней таблице
url = 'https://drive.google.com/file/d/1Q89QFO8eNumV6PwHeySnJ7g7kFA4PL0H/view?usp=sharing'
url='https://drive.google.com/uc?id=' + url.split('/')[-2]
df = pd.read_csv(url)
df.head()
| date | customer_id | order_id | product | quantity | price | |
|---|---|---|---|---|---|---|
| 0 | 2018100100 | ee47d746-6d2f-4d3c-9622-c31412542920 | 68477 | Комнатное растение в горшке Алое Вера, d12, h30 | 1 | 142.0 |
| 1 | 2018100100 | ee47d746-6d2f-4d3c-9622-c31412542920 | 68477 | Комнатное растение в горшке Кофе Арабика, d12,... | 1 | 194.0 |
| 2 | 2018100100 | ee47d746-6d2f-4d3c-9622-c31412542920 | 68477 | Радермахера d-12 см h-20 см | 1 | 112.0 |
| 3 | 2018100100 | ee47d746-6d2f-4d3c-9622-c31412542920 | 68477 | Хризолидокарпус Лутесценс d-9 см | 1 | 179.0 |
| 4 | 2018100100 | ee47d746-6d2f-4d3c-9622-c31412542920 | 68477 | Циперус Зумула d-12 см h-25 см | 1 | 112.0 |
# Выведим основную информацию о датафрейме с помощью метода `info()`
# Изучим столбцы и их типы
df.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 6737 entries, 0 to 6736 Data columns (total 6 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 date 6737 non-null int64 1 customer_id 6737 non-null object 2 order_id 6737 non-null int64 3 product 6737 non-null object 4 quantity 6737 non-null int64 5 price 6737 non-null float64 dtypes: float64(1), int64(3), object(2) memory usage: 315.9+ KB
# Узнаем количество строк и столбцов в датафрейме
df.shape
(6737, 6)
# Просмотр сводной статистики
df.describe().T
| count | mean | std | min | 25% | 50% | 75% | max | |
|---|---|---|---|---|---|---|---|---|
| date | 6737.0 | 2.018855e+09 | 385518.465620 | 2.018100e+09 | 2.019020e+09 | 2.019050e+09 | 2.019061e+09 | 2.019103e+09 |
| order_id | 6737.0 | 4.312895e+04 | 27899.414662 | 1.262400e+04 | 1.482700e+04 | 6.850300e+04 | 7.050400e+04 | 7.316400e+04 |
| quantity | 6737.0 | 2.501559e+00 | 15.266478 | 1.000000e+00 | 1.000000e+00 | 1.000000e+00 | 1.000000e+00 | 1.000000e+03 |
| price | 6737.0 | 4.620285e+02 | 871.296064 | 9.000000e+00 | 1.010000e+02 | 1.350000e+02 | 3.980000e+02 | 1.491700e+04 |
Перед обработкой данных выведим на экран исходные данные, чтобы понимать дальнейшие изменения.
# Количество строк
print('Количество строк:', len(df))
# Количество уникальных покупателей
print('Количество уникальных покупателей:', len(df['customer_id'].unique()))
# Количество уникальных заказов
print('Количество уникальных заказов:', len(df['order_id'].unique()))
# Количество проданного товара
print('Количество проданного товара:', (df['quantity'].sum()))
# Общая выручка
df['revenue']= (df['price']* df['quantity']) # добавим новый столбец с выручкой
print('Общая выручка:',df['revenue'].sum())
# Средний чек заказа
order_sum = df.groupby('order_id').agg({'revenue': 'sum'}).reset_index()
print('Средний чек заказа:', order_sum['revenue'].mean())
Количество строк: 6737 Количество уникальных покупателей: 2451 Количество уникальных заказов: 2784 Количество проданного товара: 16853 Общая выручка: 4851280.0 Средний чек заказа: 1742.557471264368
# Проверим наличие пропусков в каждом столбце(%)
pd.DataFrame(round(df.isna().mean()*100,)).style.background_gradient('BuPu')
| 0 | |
|---|---|
| date | 0.000000 |
| customer_id | 0.000000 |
| order_id | 0.000000 |
| product | 0.000000 |
| quantity | 0.000000 |
| price | 0.000000 |
| revenue | 0.000000 |
# Проверим наличие дубликатов
df.duplicated().sum()
0
# Удалим неявные дубликаты при совпадении трех столбцов
df_up= df.drop_duplicates(subset=['customer_id', 'order_id', 'product'], keep='last')
# Узнаем количество строк и столбцов в датафрейме
df_up.shape
(4851, 7)
print('Всего удаленно строк:', len(df)-len(df_up))
Всего удаленно строк: 1886
# Изучим столбцы и их типы
df_up.info()
<class 'pandas.core.frame.DataFrame'> Int64Index: 4851 entries, 0 to 6736 Data columns (total 7 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 date 4851 non-null int64 1 customer_id 4851 non-null object 2 order_id 4851 non-null int64 3 product 4851 non-null object 4 quantity 4851 non-null int64 5 price 4851 non-null float64 6 revenue 4851 non-null float64 dtypes: float64(2), int64(3), object(2) memory usage: 303.2+ KB
Заменим тип данных в столбце date на datetime64
df_up['date'] = pd.to_datetime(df['date'], format = '%Y%m%d%H')
Добавим к таблице новые столбцы:
year- год; quartel- квартал; month- месяц; week- день недели; day- день; hour- часdf_up['year'] = df_up['date'].astype('datetime64[Y]')
df_up['quartel'] = df_up['date'].dt.quarter
df_up['month'] = df_up['date'].astype('datetime64[M]')
df_up['week'] = df_up['date'].dt.day_name()
df_up['day'] = df_up['date'].astype('datetime64[D]')
df_up['hour'] = df_up['date'].dt.hour
# Проверим столбцы и их типы
df_up.info()
<class 'pandas.core.frame.DataFrame'> Int64Index: 4851 entries, 0 to 6736 Data columns (total 13 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 date 4851 non-null datetime64[ns] 1 customer_id 4851 non-null object 2 order_id 4851 non-null int64 3 product 4851 non-null object 4 quantity 4851 non-null int64 5 price 4851 non-null float64 6 revenue 4851 non-null float64 7 year 4851 non-null datetime64[ns] 8 quartel 4851 non-null int64 9 month 4851 non-null datetime64[ns] 10 week 4851 non-null object 11 day 4851 non-null datetime64[ns] 12 hour 4851 non-null int64 dtypes: datetime64[ns](4), float64(2), int64(4), object(3) memory usage: 530.6+ KB
# Проверим столбцы
df_up.head()
| date | customer_id | order_id | product | quantity | price | revenue | year | quartel | month | week | day | hour | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 2018-10-01 | ee47d746-6d2f-4d3c-9622-c31412542920 | 68477 | Комнатное растение в горшке Алое Вера, d12, h30 | 1 | 142.0 | 142.0 | 2018-01-01 | 4 | 2018-10-01 | Monday | 2018-10-01 | 0 |
| 1 | 2018-10-01 | ee47d746-6d2f-4d3c-9622-c31412542920 | 68477 | Комнатное растение в горшке Кофе Арабика, d12,... | 1 | 194.0 | 194.0 | 2018-01-01 | 4 | 2018-10-01 | Monday | 2018-10-01 | 0 |
| 2 | 2018-10-01 | ee47d746-6d2f-4d3c-9622-c31412542920 | 68477 | Радермахера d-12 см h-20 см | 1 | 112.0 | 112.0 | 2018-01-01 | 4 | 2018-10-01 | Monday | 2018-10-01 | 0 |
| 3 | 2018-10-01 | ee47d746-6d2f-4d3c-9622-c31412542920 | 68477 | Хризолидокарпус Лутесценс d-9 см | 1 | 179.0 | 179.0 | 2018-01-01 | 4 | 2018-10-01 | Monday | 2018-10-01 | 0 |
| 4 | 2018-10-01 | ee47d746-6d2f-4d3c-9622-c31412542920 | 68477 | Циперус Зумула d-12 см h-25 см | 1 | 112.0 | 112.0 | 2018-01-01 | 4 | 2018-10-01 | Monday | 2018-10-01 | 0 |
Проверка столбца quantity
df_up['quantity'].describe(percentiles=[0.1, 0.5, 0.6
, 0.7, 0.8, 0.88, 0.9, 0.95]).T
count 4851.000000 mean 2.835910 std 17.642155 min 1.000000 10% 1.000000 50% 1.000000 60% 1.000000 70% 1.000000 80% 2.000000 88% 3.000000 90% 3.000000 95% 8.000000 max 1000.000000 Name: quantity, dtype: float64
#Найдем заказ с количеством 1000
df_up.sort_values(by='quantity', ascending=False)[:5]
| date | customer_id | order_id | product | quantity | price | revenue | year | quartel | month | week | day | hour | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 5456 | 2019-06-18 15:00:00 | 312e9a3e-5fca-43ff-a6a1-892d2b2d5ba6 | 71743 | Вантуз с деревянной ручкой d14 см красный, Bur... | 1000 | 675.0 | 675000.0 | 2019-01-01 | 2 | 2019-06-01 | Tuesday | 2019-06-18 | 15 |
| 5071 | 2019-06-11 07:00:00 | 146cd9bf-a95c-4afb-915b-5f6684b17444 | 71668 | Вешалки мягкие для деликатных вещей 3 шт шоколад | 334 | 148.0 | 49432.0 | 2019-01-01 | 2 | 2019-06-01 | Tuesday | 2019-06-11 | 7 |
| 3961 | 2019-05-20 21:00:00 | 5d189e88-d4d6-4eac-ab43-fa65a3c4d106 | 71478 | Муляж ЯБЛОКО 9 см красное | 300 | 51.0 | 15300.0 | 2019-01-01 | 2 | 2019-05-01 | Monday | 2019-05-20 | 21 |
| 1158 | 2018-12-10 14:00:00 | a984c5b7-ff7e-4647-b84e-ef0b85a2762d | 69289 | Ручка-скоба РС-100 белая *Трибатрон*, 1108035 | 200 | 29.0 | 5800.0 | 2018-01-01 | 4 | 2018-12-01 | Monday | 2018-12-10 | 14 |
| 568 | 2018-11-01 08:00:00 | aa42dc38-780f-4b50-9a65-83b6fa64e766 | 68815 | Муляж ЯБЛОКО 9 см красное | 170 | 51.0 | 8670.0 | 2018-01-01 | 4 | 2018-11-01 | Thursday | 2018-11-01 | 8 |
#Удаляем выбросы, так как это были оптовые покупки
df_up = df_up[(df_up['quantity'] < 1000)]
# Проверим
df_up['quantity'].describe(percentiles=[0.1, 0.5, 0.6
, 0.7, 0.8, 0.88, 0.9, 0.95]).T
count 4850.000000 mean 2.630309 std 10.305702 min 1.000000 10% 1.000000 50% 1.000000 60% 1.000000 70% 1.000000 80% 2.000000 88% 3.000000 90% 3.000000 95% 8.000000 max 334.000000 Name: quantity, dtype: float64
# Построим диаграмму размаха("ящик с усами")
fig = plt.figure(figsize=(15, 5))
ax = plt.subplot(2, 1,2)
ax.boxplot(df_up['quantity'], False, sym='rs', vert=False, whis=0.5, positions=[0], widths=[0.3])
plt.tight_layout()
plt.show()
Проверка столбца price
df_up['price'].describe(percentiles=[0.1, 0.5, 0.6
, 0.7, 0.8, 0.88, 0.9, 0.95]).T
count 4850.000000 mean 516.060206 std 946.179939 min 9.000000 10% 38.000000 50% 150.000000 60% 188.000000 70% 374.000000 80% 712.000000 88% 1124.000000 90% 1424.000000 95% 2198.750000 max 14917.000000 Name: price, dtype: float64
#Найдем заказ с максимальной ценой
df_up.sort_values(by='price', ascending=False)[:5]
| date | customer_id | order_id | product | quantity | price | revenue | year | quartel | month | week | day | hour | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 5992 | 2019-07-29 17:00:00 | 0d87f4ae-465a-4fac-81e6-5d629761783e | 72139 | Сушилка уличная Leifheit 85210 LINOMATIC V 400... | 1 | 14917.0 | 14917.0 | 2019-01-01 | 3 | 2019-07-01 | Monday | 2019-07-29 | 17 |
| 2697 | 2019-04-05 19:00:00 | c0c60544-3a99-49d0-8a8e-cf7f293c22cb | 71035 | Сумка-тележка хозяйственная Andersen Royal Sho... | 1 | 8737.0 | 8737.0 | 2019-01-01 | 2 | 2019-04-01 | Friday | 2019-04-05 | 19 |
| 1981 | 2019-02-24 10:00:00 | ac250053-a236-467a-97d2-ddbb9bf4a1ba | 70423 | Сумка-тележка хозяйственная Andersen Alu Star ... | 1 | 8437.0 | 8437.0 | 2019-01-01 | 1 | 2019-02-01 | Sunday | 2019-02-24 | 10 |
| 2997 | 2019-04-21 16:00:00 | 19d904d8-8d16-476d-8f66-b2a3b7a23660 | 71227 | Сумка-тележка хозяйственная Rolser MNB019 rojo... | 1 | 8077.0 | 8077.0 | 2019-01-01 | 2 | 2019-04-01 | Sunday | 2019-04-21 | 16 |
| 6629 | 2019-10-16 15:00:00 | d5584388-ffbe-42fd-a746-a98828ec919f | 72992 | Стремянка 7 ступенчатая Hailo 8040-707 XXL 13 ... | 1 | 7724.0 | 7724.0 | 2019-01-01 | 4 | 2019-10-01 | Wednesday | 2019-10-16 | 15 |
# Построим диаграмму размаха("ящик с усами")
fig = plt.figure(figsize=(15, 5))
ax = plt.subplot(2, 1,2)
ax.boxplot(df_up['price'], False, sym='rs', vert=False, whis=0.5, positions=[0], widths=[0.3])
plt.tight_layout()
plt.show()
**Наблюдение:** В данных нашли оптовый заказ 71743, который удалили. По ценам никаких выбросов нет.
Проверим задвоение заказов на несколько покупателей
dupl_user = df_up.groupby('order_id').agg({'customer_id': 'nunique', 'revenue': 'sum', 'quantity': 'sum'}).reset_index()
dupl_user = dupl_user.query('customer_id > 1')
print('Всего задвоений покупателей:', len(dupl_user))
Всего задвоений покупателей: 29
data_check_dup= (df_up.groupby(['order_id', 'product']).agg({'customer_id': 'nunique'}).reset_index().query('customer_id > 1')['order_id'])
data_check_dup
1657 14872 1658 14872 1659 14872 2331 68785 2819 69283 2839 69310 2854 69345 2884 69410 2924 69485 2957 69531 3092 69833 3226 70114 3471 70542 3523 70631 3570 70726 3609 70808 3659 70903 3679 70946 3741 71054 3829 71226 3936 71461 3945 71480 3970 71542 3987 71571 4031 71648 4035 71663 4299 72188 4601 72778 4608 72790 4642 72845 4709 72950 Name: order_id, dtype: int64
data = df_up.query('order_id not in @dupl_user')
data.shape
(4850, 13)
data = df_up.query('order_id not in @data_check_dup')
data.shape
(4784, 13)
**Наблюдение:** После удаления дубликатов в разрезе покупателей и заказов мы имеем 4784 проданных товаров.
После обработи данных выведим на экран новые данные
# Количество строк
print('Количесво строк таблицы: {}, данные после обработки уменьшилось на {:.2%}'.format(len(data), 1 - (len(data) / len(df))))
# Количество уникальных покупателей
print('Кол-во уникальных покупателей: {}, данные после обработки уменьшилось на {:.2%}'
.format(len(data['customer_id'].unique()), 1 - (len(data['customer_id'].unique()) / len(df['customer_id'].unique()))))
# Количество уникальных заказов
print('Количество уникальных заказов:{}, данные после обработки уменьшилось на {:.2%}'
.format(len(data['order_id'].unique()), 1 - (len(data['order_id'].unique()) / len(df['order_id'].unique()))))
# Количество проданного товара
print('Количество проданного товара:{}, данные после обработки уменьшилось на {:.2%}'
.format(data['quantity'].sum(), 1 - ((data['quantity'].sum()) / (df['quantity'].sum()))))
# Общая выручка
print('Общая выручка:{}, данные после обработки уменьшилось на {:.2%}'
.format(data['revenue'].sum(), 1 - (data['revenue'].sum() / df['revenue'].sum())))
# Средний чек заказа
order_sum = df.groupby('order_id').agg({'revenue': 'sum'}).reset_index()
order_sum_after = data.groupby('order_id').agg({'revenue': 'sum'}).reset_index()
print('Средний чек заказа:{}, данные после обработки уменьшилось на {:.2%}'
.format(order_sum_after['revenue'].mean(), 1 - (order_sum_after['revenue'].mean()) / (order_sum['revenue'].mean())))
Количесво строк таблицы: 4784, данные после обработки уменьшилось на 28.99% Кол-во уникальных покупателей: 2393, данные после обработки уменьшилось на 2.37% Количество уникальных заказов:2754, данные после обработки уменьшилось на 1.08% Количество проданного товара:12437, данные после обработки уменьшилось на 26.20% Общая выручка:3411515.0, данные после обработки уменьшилось на 29.68% Средний чек заказа:1238.7490922294844, данные после обработки уменьшилось на 28.91%
data.shape
(4784, 13)
ВЫВОД:
# Выгрузим очищенный датафрэйм для табло
data.to_csv('data.csv', index = False)
Эти данные нам помогут распределить товары на группы
# Группируем данные по количеству заказов
quan_count = data.groupby('quantity').agg({'order_id': 'count'}).reset_index()
# Переименовываем название столбцов
quan_count.columns = ['quantity','count']
# Добавляем столбец "%", чтобы найти долю с общего объема
quan_count['%'] = round((quan_count['count']/quan_count['count'].sum())*100,2)
# Сортируем данные по убыванию
quan_count[:10].sort_values(by='count', ascending=False).style.format({'%':'{:.2f}%'})
#quan_count[:10].style.bar(subset=['count'], color='#ffe135')
| quantity | count | % | |
|---|---|---|---|
| 0 | 1 | 3758 | 78.55% |
| 1 | 2 | 423 | 8.84% |
| 2 | 3 | 138 | 2.88% |
| 3 | 4 | 101 | 2.11% |
| 9 | 10 | 70 | 1.46% |
| 4 | 5 | 67 | 1.40% |
| 5 | 6 | 41 | 0.86% |
| 6 | 7 | 20 | 0.42% |
| 7 | 8 | 14 | 0.29% |
| 8 | 9 | 7 | 0.15% |
**Наблюдение:** Основной объем продаж состоит из 1-2 позиций в заказе.
Разбиваем данные на группы для визуализации:
def group_goods(product):
try:
if 1 == product:
return 'A'
elif 2 == product:
return 'B'
elif 3 <= product <= 10:
return 'C'
elif 11 <= product <= 30:
return 'D'
elif 31 <= product <= 50:
return 'E'
elif product >= 51:
return 'F'
except:
pass
quan_count['category_quantity'] = quan_count['quantity'].apply(group_goods)
quan_count[:10];
# Группируем данные по количеству заказов
categoties_goods = quan_count.groupby('category_quantity').agg({'count': 'sum'}).reset_index()
# Переименовываем название столбцов
categoties_goods.columns = ['category_quantity','count']
# Добавляем столбец "%", чтобы найти долю с общего объема
categoties_goods['%'] = round((categoties_goods['count']/categoties_goods['count'].sum())*100,1)
# Сортируем данные по убыванию
categoties_goods.sort_values(by='count', ascending=False)
categoties_goods.style.bar(subset=['count'], color='#ffe135')
| category_quantity | count | % | |
|---|---|---|---|
| 0 | A | 3758 | 78.600000 |
| 1 | B | 423 | 8.800000 |
| 2 | C | 458 | 9.600000 |
| 3 | D | 109 | 2.300000 |
| 4 | E | 14 | 0.300000 |
| 5 | F | 22 | 0.500000 |
# Построим круговую диаграмму для определения доли каждого события
fig = go.Figure(data=[go.Pie(labels=quan_count['category_quantity'], values=quan_count['%'])])
fig.update_layout(title="Распределение количества заказов по группах (%)"
)
fig.update_traces(hoverinfo='label+percent', textinfo='value', textfont_size=10, marker=dict(line=dict(color='#000000', width=1)))
fig.show()
**Наблюдение:** Более 78% в одном заказе присутствует только 1 товар, что можно оценить, как неэффективность работы отдела продаж или отдельно продавцов. Также присутствуют заказы, где более 50 товаров в заказе, есть предположение, что это были или оптовые покупатели или технический сбой в программе.
# Группируем данные по месяцам
revenue_month = data.groupby('month').agg({'order_id': 'count', 'revenue': 'sum'}).reset_index()
# Переименовываем название столбцов
revenue_month.columns = ['month','orders_Q', 'orders_revenue']
revenue_month.style.bar(subset=['orders_revenue'], color='#ffe135')
| month | orders_Q | orders_revenue | |
|---|---|---|---|
| 0 | 2018-10-01 00:00:00 | 478 | 350374.000000 |
| 1 | 2018-11-01 00:00:00 | 430 | 359772.000000 |
| 2 | 2018-12-01 00:00:00 | 303 | 341910.000000 |
| 3 | 2019-01-01 00:00:00 | 184 | 234117.000000 |
| 4 | 2019-02-01 00:00:00 | 359 | 304446.000000 |
| 5 | 2019-03-01 00:00:00 | 407 | 249537.000000 |
| 6 | 2019-04-01 00:00:00 | 673 | 316122.000000 |
| 7 | 2019-05-01 00:00:00 | 685 | 228814.000000 |
| 8 | 2019-06-01 00:00:00 | 324 | 233624.000000 |
| 9 | 2019-07-01 00:00:00 | 311 | 226361.000000 |
| 10 | 2019-08-01 00:00:00 | 200 | 180910.000000 |
| 11 | 2019-09-01 00:00:00 | 216 | 177922.000000 |
| 12 | 2019-10-01 00:00:00 | 214 | 207606.000000 |
# Найдем среднюю выручку
month_aver = round(revenue_month['orders_revenue'].sum()/12, 2)
print('Средняя выручка в месяц в разрезе года:', month_aver)
Средняя выручка в месяц в разрезе года: 284292.92
# Построим график
fig = px.bar(revenue_month, y='orders_revenue', x= 'month')
# оформляем график
fig.update_layout(title='Ежемесячная выручка(руб.)',
xaxis_title='Месяц',
yaxis_title='Выручка(руб.)',
width=800, # указываем размеры графика
height=500)
# добавляем ось X
fig.add_hline(y=month_aver, line_dash="dash", line_color="grey")
fig.show()
Найдем среднее количество заказов и среднюю выручку по месяцам
# Сгруппируем данные
month_user = data.groupby('month')['customer_id'].agg(['nunique']).reset_index()
# Объединим таблицы по месяцу
user_aver = month_user.merge(revenue_month, on = ['month'])
# Добавим столбец со сред кол заказов на одного покупателя
user_aver['Среднее кол-во заказов'] = (user_aver['orders_Q'] / user_aver['nunique']).round(2)
# Добавим столбец со сред выручной на одного покупателя
user_aver['Средняя сумма заказа пок-ля'] = (user_aver['orders_revenue'] /user_aver['nunique']).round(2)
user_aver.columns = ['Месяц','Q покупателей','Q заказов', 'Выручка', 'Среднее кол-во заказов пок-ля', 'Средняя выручка заказа пок-ля']
user_aver
| Месяц | Q покупателей | Q заказов | Выручка | Среднее кол-во заказов пок-ля | Средняя выручка заказа пок-ля | |
|---|---|---|---|---|---|---|
| 0 | 2018-10-01 | 178 | 478 | 350374.0 | 2.69 | 1968.39 |
| 1 | 2018-11-01 | 178 | 430 | 359772.0 | 2.42 | 2021.19 |
| 2 | 2018-12-01 | 226 | 303 | 341910.0 | 1.34 | 1512.88 |
| 3 | 2019-01-01 | 151 | 184 | 234117.0 | 1.22 | 1550.44 |
| 4 | 2019-02-01 | 244 | 359 | 304446.0 | 1.47 | 1247.73 |
| 5 | 2019-03-01 | 228 | 407 | 249537.0 | 1.79 | 1094.46 |
| 6 | 2019-04-01 | 235 | 673 | 316122.0 | 2.86 | 1345.20 |
| 7 | 2019-05-01 | 162 | 685 | 228814.0 | 4.23 | 1412.43 |
| 8 | 2019-06-01 | 150 | 324 | 233624.0 | 2.16 | 1557.49 |
| 9 | 2019-07-01 | 185 | 311 | 226361.0 | 1.68 | 1223.57 |
| 10 | 2019-08-01 | 165 | 200 | 180910.0 | 1.21 | 1096.42 |
| 11 | 2019-09-01 | 171 | 216 | 177922.0 | 1.26 | 1040.48 |
| 12 | 2019-10-01 | 169 | 214 | 207606.0 | 1.27 | 1228.44 |
**Наблюдение:** Объем выручки пришелся на июнь, хотя сентябрь показал "плохой" месяц по выручке. Лидерами по среднему количеству заказов оказались месяца- май и апрель. Лидерами по среднему чеку стали- июнь и ноябрь.
# Группируем данные по дням недели
revenue_week = data.groupby('week').agg({'order_id': 'count', 'revenue': 'sum'}).reset_index()
# Переименовываем название столбцов
revenue_week.columns = ['week','orders_Q', 'orders_revenue']
revenue_week.style.bar(subset=['orders_revenue'], color='#ffe135')
| week | orders_Q | orders_revenue | |
|---|---|---|---|
| 0 | Friday | 600 | 507445.000000 |
| 1 | Monday | 894 | 584357.000000 |
| 2 | Saturday | 499 | 260282.000000 |
| 3 | Sunday | 517 | 337947.000000 |
| 4 | Thursday | 756 | 537790.000000 |
| 5 | Tuesday | 775 | 676060.000000 |
| 6 | Wednesday | 743 | 507634.000000 |
Найдем среднее количество заказов и среднюю выручку по дням недели
# Сгруппируем данные
week_user = data.groupby('week')['customer_id'].agg(['nunique']).reset_index()
# Объединим таблицы по месяцу
user_aver_week = week_user.merge(revenue_week, on = ['week'])
# Добавим столбец со сред кол заказов на одного покупателя
user_aver_week['Среднее кол-во заказов'] = (user_aver_week['orders_Q'] / user_aver_week['nunique']).round(2)
# Добавим столбец со сред выручной на одного покупателя
user_aver_week['Средняя сумма заказа пок-ля'] = (user_aver_week['orders_revenue'] /user_aver_week['nunique']).round(2)
user_aver_week.columns = ['Месяц','Q покупателей','Q заказов', 'Выручка', 'Среднее кол-во заказов пок-ля', 'Средняя выручка заказа пок-ля']
user_aver_week.style.bar(subset=['Среднее кол-во заказов пок-ля', 'Средняя выручка заказа пок-ля'], color='#ffe135')
| Месяц | Q покупателей | Q заказов | Выручка | Среднее кол-во заказов пок-ля | Средняя выручка заказа пок-ля | |
|---|---|---|---|---|---|---|
| 0 | Friday | 320 | 600 | 507445.000000 | 1.880000 | 1585.770000 |
| 1 | Monday | 430 | 894 | 584357.000000 | 2.080000 | 1358.970000 |
| 2 | Saturday | 234 | 499 | 260282.000000 | 2.130000 | 1112.320000 |
| 3 | Sunday | 325 | 517 | 337947.000000 | 1.590000 | 1039.840000 |
| 4 | Thursday | 387 | 756 | 537790.000000 | 1.950000 | 1389.640000 |
| 5 | Tuesday | 429 | 775 | 676060.000000 | 1.810000 | 1575.900000 |
| 6 | Wednesday | 382 | 743 | 507634.000000 | 1.950000 | 1328.880000 |
**Наблюдение:** Объем выручки пришелся на вторник, а суббота показала "плохой" день недели по выручке. Лидерами по среднему количеству заказов оказались дни недели- воскресенье и понедельник. Лидерами по среднему чеку стали- вторник и пятница.
# Группируем данные по времени
revenue_hours = data.groupby('hour').agg({'order_id': 'count', 'revenue': 'sum'}).reset_index()
# Переименовываем название столбцов
revenue_hours.columns = ['hour','orders_Q', 'orders_revenue']
revenue_hours.style.bar(subset=['orders_revenue'], color='#ffe135')
| hour | orders_Q | orders_revenue | |
|---|---|---|---|
| 0 | 0 | 42 | 31392.000000 |
| 1 | 1 | 41 | 21491.000000 |
| 2 | 2 | 16 | 22405.000000 |
| 3 | 3 | 8 | 6415.000000 |
| 4 | 4 | 28 | 18937.000000 |
| 5 | 5 | 36 | 19232.000000 |
| 6 | 6 | 47 | 53945.000000 |
| 7 | 7 | 88 | 146112.000000 |
| 8 | 8 | 281 | 179326.000000 |
| 9 | 9 | 328 | 229504.000000 |
| 10 | 10 | 343 | 218823.000000 |
| 11 | 11 | 401 | 282890.000000 |
| 12 | 12 | 323 | 237150.000000 |
| 13 | 13 | 405 | 304536.000000 |
| 14 | 14 | 386 | 205529.000000 |
| 15 | 15 | 326 | 243530.000000 |
| 16 | 16 | 327 | 220045.000000 |
| 17 | 17 | 270 | 243551.000000 |
| 18 | 18 | 165 | 160943.000000 |
| 19 | 19 | 216 | 147099.000000 |
| 20 | 20 | 197 | 117169.000000 |
| 21 | 21 | 240 | 147158.000000 |
| 22 | 22 | 188 | 118679.000000 |
| 23 | 23 | 82 | 35654.000000 |
# Построим график
fig = px.bar(revenue_hours, y='orders_revenue', x= 'hour')
# оформляем график
fig.update_layout(title='Часовая выручка(руб.)',
xaxis_title='Час',
yaxis_title='Выручка(руб.)',
width=1000, # указываем размеры графика
height=500)
# добавляем ось X
#fig.add_hline(y=month_aver, line_dash="dash", line_color="grey")
fig.show()
Найдем среднее количество заказов и среднюю выручку по часам
# Сгруппируем данные по времени
hour_user = data.groupby('hour')['customer_id'].agg(['nunique']).reset_index()
# Объединим таблицы по месяцу
user_aver_hour = hour_user.merge(revenue_hours, on = ['hour'])
# Добавим столбец со сред кол заказов на одного покупателя
user_aver_hour['Среднее кол-во заказов'] = (user_aver_hour['orders_Q'] / user_aver_hour['nunique']).round(2)
# Добавим столбец со сред выручной на одного покупателя
user_aver_hour['Средняя сумма заказа пок-ля'] = (user_aver_hour['orders_revenue'] /user_aver_hour['nunique']).round(2)
user_aver_hour.columns = ['Час','Q покупателей','Q заказов', 'Выручка', 'Среднее кол-во заказов пок-ля', 'Средняя выручка заказа пок-ля']
user_aver_hour.style.bar(subset=['Среднее кол-во заказов пок-ля', 'Средняя выручка заказа пок-ля'], color='#ffe135')
| Час | Q покупателей | Q заказов | Выручка | Среднее кол-во заказов пок-ля | Средняя выручка заказа пок-ля | |
|---|---|---|---|---|---|---|
| 0 | 0 | 30 | 42 | 31392.000000 | 1.400000 | 1046.400000 |
| 1 | 1 | 15 | 41 | 21491.000000 | 2.730000 | 1432.730000 |
| 2 | 2 | 13 | 16 | 22405.000000 | 1.230000 | 1723.460000 |
| 3 | 3 | 8 | 8 | 6415.000000 | 1.000000 | 801.880000 |
| 4 | 4 | 17 | 28 | 18937.000000 | 1.650000 | 1113.940000 |
| 5 | 5 | 18 | 36 | 19232.000000 | 2.000000 | 1068.440000 |
| 6 | 6 | 32 | 47 | 53945.000000 | 1.470000 | 1685.780000 |
| 7 | 7 | 62 | 88 | 146112.000000 | 1.420000 | 2356.650000 |
| 8 | 8 | 121 | 281 | 179326.000000 | 2.320000 | 1482.030000 |
| 9 | 9 | 167 | 328 | 229504.000000 | 1.960000 | 1374.280000 |
| 10 | 10 | 195 | 343 | 218823.000000 | 1.760000 | 1122.170000 |
| 11 | 11 | 201 | 401 | 282890.000000 | 2.000000 | 1407.410000 |
| 12 | 12 | 190 | 323 | 237150.000000 | 1.700000 | 1248.160000 |
| 13 | 13 | 191 | 405 | 304536.000000 | 2.120000 | 1594.430000 |
| 14 | 14 | 186 | 386 | 205529.000000 | 2.080000 | 1104.990000 |
| 15 | 15 | 175 | 326 | 243530.000000 | 1.860000 | 1391.600000 |
| 16 | 16 | 149 | 327 | 220045.000000 | 2.190000 | 1476.810000 |
| 17 | 17 | 144 | 270 | 243551.000000 | 1.880000 | 1691.330000 |
| 18 | 18 | 106 | 165 | 160943.000000 | 1.560000 | 1518.330000 |
| 19 | 19 | 124 | 216 | 147099.000000 | 1.740000 | 1186.280000 |
| 20 | 20 | 119 | 197 | 117169.000000 | 1.660000 | 984.610000 |
| 21 | 21 | 142 | 240 | 147158.000000 | 1.690000 | 1036.320000 |
| 22 | 22 | 102 | 188 | 118679.000000 | 1.840000 | 1163.520000 |
| 23 | 23 | 49 | 82 | 35654.000000 | 1.670000 | 727.630000 |
**Наблюдение:** Объем заказов пришелся на промежуток времени с 8 до 17 часов, большая часть покупателей пришлась на промежуток времени с 9 до 15 часов. Наибольшая выручка пришлась на 15 часов.
# Сгруппируем данные по дню
day_order = data.groupby(['day']).agg({'order_id':'nunique', 'revenue': 'sum'}).reset_index()
# Переименовываем название столбцов
day_order.columns = ['day', 'Q_orders','sum_orders']
day_order.head()
| day | Q_orders | sum_orders | |
|---|---|---|---|
| 0 | 2018-10-01 | 9 | 8261.0 |
| 1 | 2018-10-02 | 14 | 16746.0 |
| 2 | 2018-10-03 | 8 | 10574.0 |
| 3 | 2018-10-04 | 11 | 25170.0 |
| 4 | 2018-10-05 | 6 | 4036.0 |
Построим гистограмму распределения заказов
day_orders_sum = data.groupby(['order_id']).agg({'revenue': 'sum'}).reset_index()
day_orders_sum.columns = ['orders', 'count_sum']
day_orders_sum.head()
| orders | count_sum | |
|---|---|---|
| 0 | 12624 | 375.0 |
| 1 | 13547 | 684.0 |
| 2 | 14480 | 359.0 |
| 3 | 14481 | 600.0 |
| 4 | 14482 | 376.0 |
# Просмотр сводной статистики
day_orders_sum['count_sum'].reset_index().describe(percentiles=[0.1, 0.5, 0.6, 0.7, 0.8, 0.9, 0.95, 0.99]).T
| count | mean | std | min | 10% | 50% | 60% | 70% | 80% | 90% | 95% | 99% | max | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| index | 2754.0 | 1376.500000 | 795.155645 | 0.0 | 275.3 | 1376.5 | 1651.8 | 1927.1 | 2202.4 | 2477.7 | 2615.35 | 2725.47 | 2753.0 |
| count_sum | 2754.0 | 1238.749092 | 2239.602191 | 14.0 | 134.0 | 686.0 | 899.0 | 1162.0 | 1705.8 | 2774.0 | 3908.10 | 7792.62 | 49432.0 |
# Построим гистограмму по дате
fig = px.histogram(day_orders_sum.query('count_sum <= 8000'), x="count_sum", nbins=50)
fig.update_layout(title_text='Распределение суммы заказов', xaxis_title="Сумма заказа", yaxis_title="Количество заказов")
fig.show()
**Наблюдение:** Согласно гистограмме видно, что 90% заказов ниже 2770 руб., 50% заказов ниже 690 руб.
top10_goods = data.groupby(['product']).agg({'order_id': 'count', 'revenue': 'sum'}).reset_index()
top10_goods.sort_values(by=['revenue'], ascending=False)[:10]
| product | order_id | revenue | |
|---|---|---|---|
| 1188 | Простынь вафельная 200х180 см WELLNESS RW180-0... | 2 | 53232.0 |
| 1611 | Сумка-тележка 2-х колесная Gimi Argo синяя | 47 | 50405.0 |
| 190 | Вешалки мягкие для деликатных вещей 3 шт шоколад | 2 | 49596.0 |
| 1888 | Тележка багажная DELTA ТБР-22 синий грузоподъе... | 3 | 33992.0 |
| 831 | Муляж ЯБЛОКО 9 см красное | 6 | 32702.0 |
| 870 | Набор ножей Attribute CHEF 5 предметов AKF522 | 1 | 29248.0 |
| 1637 | Сумка-тележка хозяйственная Andersen Scala Sho... | 5 | 28045.0 |
| 1939 | Урна уличная "Гео", Hobbyka/Хоббика, 59*37,5см... | 1 | 24370.0 |
| 131 | Веник сорго с деревянной ручкой с 4-мя швами, ... | 2 | 20010.0 |
| 1622 | Сумка-тележка 3-х колесная Gimi Tris Floral синяя | 7 | 18893.0 |
**Наблюдение:** Наибольший доход принесли такие позиции, как простынь вафельная, сумка-тележка и вешалки мягкие.
out10_goods = data.groupby(['product']).agg({'order_id': 'count', 'revenue': 'sum'}).reset_index()
out10_goods.sort_values(by=['revenue'], ascending=True)[:10]
| product | order_id | revenue | |
|---|---|---|---|
| 731 | Львиный зев Волшебный ковер 0,05 г 4660010779639 | 1 | 9.0 |
| 313 | Горох Амброзия 10,0 г 4660010772616 | 1 | 9.0 |
| 2054 | Цинния Коралловая красавица 0,2 г 4660010773323 | 1 | 10.0 |
| 940 | Огурец Засолочный 0,3 г 4660010776102 | 1 | 10.0 |
| 909 | Незабудка смесь 0,1 г 4650091480340 | 1 | 10.0 |
| 2055 | Цинния Оранжевый король 0,5 г 4660010770520 | 1 | 10.0 |
| 766 | Морковь Детская сладость 2 г 4660010775921 | 1 | 10.0 |
| 1053 | Петрушка Итальянский гигант 2 г 4660010776553 | 1 | 10.0 |
| 693 | Лаватера Монт Блан 0,3 г 4660010778588 | 1 | 11.0 |
| 433 | Календула Суприм 0,5 г 4650091480227 | 1 | 11.0 |
**Наблюдение:** Неспросовыми товарами оказались волшебный ковер, горох Амброзия, Цинния Коралловая и огурец. Возможно стоит их убрать из ассортимента.
# Заменим названия товаров
data['product'] = data['product'].str.replace('Мусорный контейнер в ванную комнату BOWL-SHINY полистирол 14х16 см белый, Spirella, 1014964',
'Контейнер мусорный в ванную комнату BOWL-SHINY полистирол 14х16 см белый, Spirella, 1014964')
data['product'] = data['product'].str.replace('Этажерка цветочная пластиковая ЛИВИЯ 5 кашпо М-пластика M3140',
'Цветочная этажерка пластиковая ЛИВИЯ 5 кашпо М-пластика M3140')
data['product'] = data['product'].str.replace('Ящик почтовый металлический с врезным замком Почта 1205250',
'Почтовый ящик металлический с врезным замком Почта 1205250')
data['product'] = data['product'].str.replace('Набор ковров для ванной комнаты, "Офелия", 40х50/50х80см., серый/бежевый, NTBM50804050-26-190',
'Ковры набор для ванной комнаты,"Офелия", 40х50/50х80см., серый/бежевый, NTBM50804050-26-190')
Выдедим первое слово в новый столбец first_name
data['first_name'] = data['product'].apply(lambda x: x.split(' ')[0])
# Приведем к нижнему регистру
data['first_name'] = data['first_name'].str.lower()
data[['first_name','product']].head(10)
| first_name | product | |
|---|---|---|
| 0 | комнатное | Комнатное растение в горшке Алое Вера, d12, h30 |
| 1 | комнатное | Комнатное растение в горшке Кофе Арабика, d12,... |
| 2 | радермахера | Радермахера d-12 см h-20 см |
| 3 | хризолидокарпус | Хризолидокарпус Лутесценс d-9 см |
| 4 | циперус | Циперус Зумула d-12 см h-25 см |
| 5 | шеффлера | Шеффлера Лузеана d-9 см |
| 6 | юкка | Юкка нитчатая d-12 см h-25-35 см |
| 7 | настенная | Настенная сушилка для белья Gimi Brio Super 100 |
| 8 | таз | Таз пластмассовый 21,0 л круглый "Водолей" С61... |
| 9 | чехол | Чехол для гладильной доски Colombo Persia Beig... |
После обработки данных в эксель, составляем список, который позволит распределить товара по категориям
# 1 Все для цветов
flowers = [
'примула','калибрахоа','фуксия','вербена','пуансеттия','фиалка',
'дыня','комнатное','базилик','бегония','бальзамин',
'бакопа','космея','мята','папоротник','календула', 'каланхое',
'тыква', 'гербера','цветущее', 'афеляндра',
'цитрофортунелла', 'пахира', 'фаленопсис', 'искусственная', 'эхеверия', 'клубника', 'многолетнее', 'кофе',
'седум', 'табак', 'спатифиллум', 'дендориум', 'калла', 'лавр', 'мирт','львиный', 'дендробиум', 'цветочная',
'искусственный','колокольчик','декоративная','подвесное', 'нивянник', 'вербейник', 'гардения', 'гортензия',
'калатея', 'алое', 'кореопсис', 'укроп', 'вигна', 'скиммия',
'колеус', 'душица', 'фатсия', 'лантана', 'кабачок','антуриум','огурец','хризантема','эвкалипт','декабрист',
'томат','гвоздика','арбуз','петрушка','цинния', 'патиссон','алиссум','азалия','тимьян', 'лобелия', 'исскуственная',
'капуста', 'газания','циперус','виола', 'хлорофитум', 'лаванда', 'розмарин', 'мимоза','мединилла', 'тагетис', 'земляника',
'астра','пеларгония','рассада','томата','пеларгония', 'роза','петуния', 'герань', 'цветок','однолетнее','флокс','цикламен',' зверобой', 'настурция', 'салат', 'осина', 'целозия', 'портулак', 'крассула',
'аргирантерум', 'хоста', 'цинерария', 'монарда', 'баклажан', 'вероника', 'сальвия', 'кориандр','лен']
# 2 Все для ванной
bathroom = ['ковры','сетка', 'ванна', 'подголовник', 'штора', 'полотенце', 'махровое','вешалка-сушилка', 'карниз','настенная','сушилка','фен','штанга','ёрш','ерш','ковш','пробка','комплект','контейнер', 'сиденье','корзина',
'бак','стакан','контейнер','халат', 'махровый']
# 3 Все для кухни
kitchen = ['мантоварка-пароварка', 'мантоварка','электроштопор', 'овощечистка','мусорный','кисточка','крышка',
'набор','просеиватель','лоток','емкость', 'тарелка', 'сахарница','салатник','кружка','банка','нож','сковорода','кастрюля','ложка','вилка', 'кухонное',
'термокружка', 'термос', 'нож', 'кувшин', 'чайный', 'миска','овсянница', 'терка','измельчитель', 'чайная', 'разделочная', 'блюдце', 'рыбочистка', 'термостакан', 'бидон', 'половник', 'толкушка',
'сервировочная', 'лопатка', 'столовый', 'сахарница', 'сотейник',
'бульонница', 'венчик', 'скалка', 'сотейник','тортница', 'tepмокружка', 'хлебница', 'блюдо', 'чайник', 'модульная',
'отделитель','весы', 'миксер', 'овощеварка', 'соковарка','котел']
# 4 Товары для хранения вещей
storage = ['полки', 'этажерка','полка', 'обувница-3', 'складной', 'вешалка','вешалки', 'вешалка-плечики', 'сумка',
'вешалка-стойка', 'комод', 'стеллаж', 'подставка', 'плечики']
# 5 Хозтовары и инвентарь
household = ['лестница-стремянка','тележка','стремянка', 'лестница', 'стремянки', 'урна-пепельница', 'муссорный',
'урна', 'стремянка-табурет', 'почтовый','сумка-тележка','таз', 'вантуз', 'швабра','ведро','щетка-утюжок',
'жестяная', 'зубная', 'шнур', 'петля', 'ящик', 'коробка', 'бельевые','новогоднее', 'кондиционер',
'корыто', 'веник', 'мыло', 'дозатор', 'шило', 'термометр',
'тряпкодержатель',
'совок', 'бензин', 'мешок', 'шпагат', 'вакумный', 'фал', 'корзинка', 'ваза',
'подарочный', 'ткань','автоматическая', 'пылесос','щетка-сметка'
, 'окномойка','щётка','щетка','паста','салфетка','насадка',
'cредство','перчатки', 'муляж', 'измерительный','сверло-фреза','крепеж',
'многофункциональный', 'петля-стрела','крючок','камнеломка','стяжка', 'стяжки',]
# 6 Все для спальни
bedroom = ['покрывало', 'простыня', 'одеяло', 'светильник','простынь', 'подушка', 'наматрасник', 'двуспальное',
'наматрацник-чехол', 'наматрацник', 'пододеяльник','наматрицник', 'чехол', 'кофр','плед','ковер', 'ковёр', 'коврик','скатерть','утюг', 'подкладка', 'подрукавник', 'рукав', 'гладильная', 'чехол'
,'покрытие','доска']
Далее мы с помощью функции распределим все товары по категориям
def categorie (row):
if row['first_name'] in flowers:
return 'Все для цветов'
if row['first_name'] in bathroom:
return 'Все для ванной'
if row['first_name'] in kitchen:
return 'Все для кухни'
if row['first_name'] in storage:
return 'Товары для хранения вещей'
if row['first_name'] in household:
return 'Хозтовары и инвентарь'
if row['first_name'] in bedroom:
return 'Все для спальни'
else:
return 'Другое'
# Добавим категории в новый столбец
data['categories_product'] = data.apply(categorie, axis=1)
data.head()
| date | customer_id | order_id | product | quantity | price | revenue | year | quartel | month | week | day | hour | first_name | categories_product | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 2018-10-01 | ee47d746-6d2f-4d3c-9622-c31412542920 | 68477 | Комнатное растение в горшке Алое Вера, d12, h30 | 1 | 142.0 | 142.0 | 2018-01-01 | 4 | 2018-10-01 | Monday | 2018-10-01 | 0 | комнатное | Все для цветов |
| 1 | 2018-10-01 | ee47d746-6d2f-4d3c-9622-c31412542920 | 68477 | Комнатное растение в горшке Кофе Арабика, d12,... | 1 | 194.0 | 194.0 | 2018-01-01 | 4 | 2018-10-01 | Monday | 2018-10-01 | 0 | комнатное | Все для цветов |
| 2 | 2018-10-01 | ee47d746-6d2f-4d3c-9622-c31412542920 | 68477 | Радермахера d-12 см h-20 см | 1 | 112.0 | 112.0 | 2018-01-01 | 4 | 2018-10-01 | Monday | 2018-10-01 | 0 | радермахера | Другое |
| 3 | 2018-10-01 | ee47d746-6d2f-4d3c-9622-c31412542920 | 68477 | Хризолидокарпус Лутесценс d-9 см | 1 | 179.0 | 179.0 | 2018-01-01 | 4 | 2018-10-01 | Monday | 2018-10-01 | 0 | хризолидокарпус | Другое |
| 4 | 2018-10-01 | ee47d746-6d2f-4d3c-9622-c31412542920 | 68477 | Циперус Зумула d-12 см h-25 см | 1 | 112.0 | 112.0 | 2018-01-01 | 4 | 2018-10-01 | Monday | 2018-10-01 | 0 | циперус | Все для цветов |
# Группируем данные по категориям
cat = data.groupby('categories_product').agg({'product': 'count'}).reset_index()\
.sort_values(by='product', ascending=False)
# Добавляем столбец "%", чтобы найти долю с общего объема по категориям
cat ['%'] = round((cat['product']/cat['product'].sum())*100,1)
# Переименовываем название столбцов
cat.columns = ['Категория','Кол-во товара', 'Доля(%)']
cat.style.bar(subset=['Доля(%)'], color='#ffe135')
| Категория | Кол-во товара | Доля(%) | |
|---|---|---|---|
| 3 | Все для цветов | 2431 | 50.800000 |
| 6 | Хозтовары и инвентарь | 817 | 17.100000 |
| 0 | Все для ванной | 481 | 10.100000 |
| 2 | Все для спальни | 415 | 8.700000 |
| 1 | Все для кухни | 311 | 6.500000 |
| 4 | Другое | 221 | 4.600000 |
| 5 | Товары для хранения вещей | 108 | 2.300000 |
cat.plot(y= 'Доля(%)', kind="pie", figsize=(7, 7), autopct='%1.1f%%', labels=cat['Категория'],)
plt.legend(bbox_to_anchor=(0.6, 0, 0.6, 0.9)) # Расположение легенды на графике
plt.title('Доля распределения категорий товара') # Название графика
plt.show()
**Наблюдение:** Согласно диаграмме, основной категорией с долей продаж в 50,7% стали товары в категории "Все для цветов", далее 17,1%- "Хозтовары и инвентарь" и 10,1%- "Все для ванной".
# Объединим таблицы по месяцу
user_aver = month_user.merge(revenue_month, on = ['month'])
# Добавим столбец со сред кол заказов на одного покупателя
user_aver['Среднее кол-во заказов'] = (user_aver['orders_Q'] / user_aver['nunique']).round(2)
# Добавим столбец со сред выручной на одного покупателя
user_aver['Средняя сумма заказа пок-ля'] = (user_aver['orders_revenue'] /user_aver['nunique']).round(2)
user_aver.columns = ['Месяц','Q покупателей','Q заказов', 'Выручка', 'Среднее кол-во заказов пок-ля', 'Средняя выручка заказа пок-ля']
user_aver
| Месяц | Q покупателей | Q заказов | Выручка | Среднее кол-во заказов пок-ля | Средняя выручка заказа пок-ля | |
|---|---|---|---|---|---|---|
| 0 | 2018-10-01 | 178 | 478 | 350374.0 | 2.69 | 1968.39 |
| 1 | 2018-11-01 | 178 | 430 | 359772.0 | 2.42 | 2021.19 |
| 2 | 2018-12-01 | 226 | 303 | 341910.0 | 1.34 | 1512.88 |
| 3 | 2019-01-01 | 151 | 184 | 234117.0 | 1.22 | 1550.44 |
| 4 | 2019-02-01 | 244 | 359 | 304446.0 | 1.47 | 1247.73 |
| 5 | 2019-03-01 | 228 | 407 | 249537.0 | 1.79 | 1094.46 |
| 6 | 2019-04-01 | 235 | 673 | 316122.0 | 2.86 | 1345.20 |
| 7 | 2019-05-01 | 162 | 685 | 228814.0 | 4.23 | 1412.43 |
| 8 | 2019-06-01 | 150 | 324 | 233624.0 | 2.16 | 1557.49 |
| 9 | 2019-07-01 | 185 | 311 | 226361.0 | 1.68 | 1223.57 |
| 10 | 2019-08-01 | 165 | 200 | 180910.0 | 1.21 | 1096.42 |
| 11 | 2019-09-01 | 171 | 216 | 177922.0 | 1.26 | 1040.48 |
| 12 | 2019-10-01 | 169 | 214 | 207606.0 | 1.27 | 1228.44 |
# Сгруппируем данные
month_category = data.pivot_table(index='categories_product', columns= 'month', values=['revenue'],\
aggfunc={'revenue': 'sum' }).reset_index()
cm = sns.light_palette("#5f9bd6", as_cmap=True)
month_category.style.background_gradient(cmap=cm)
| categories_product | revenue | |||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| month | NaT | 2018-10-01 00:00:00 | 2018-11-01 00:00:00 | 2018-12-01 00:00:00 | 2019-01-01 00:00:00 | 2019-02-01 00:00:00 | 2019-03-01 00:00:00 | 2019-04-01 00:00:00 | 2019-05-01 00:00:00 | 2019-06-01 00:00:00 | 2019-07-01 00:00:00 | 2019-08-01 00:00:00 | 2019-09-01 00:00:00 | 2019-10-01 00:00:00 |
| 0 | Все для ванной | 73656.000000 | 35282.000000 | 47196.000000 | 37088.000000 | 59539.000000 | 47284.000000 | 22512.000000 | 13439.000000 | 31700.000000 | 52218.000000 | 22573.000000 | 28983.000000 | 44970.000000 |
| 1 | Все для кухни | 34313.000000 | 69663.000000 | 32233.000000 | 29623.000000 | 41182.000000 | 23787.000000 | 7217.000000 | 9007.000000 | 4255.000000 | 9889.000000 | 12920.000000 | 17514.000000 | 5287.000000 |
| 2 | Все для спальни | 66237.000000 | 71451.000000 | 93730.000000 | 84394.000000 | 46685.000000 | 33245.000000 | 43992.000000 | 13083.000000 | 52599.000000 | 50311.000000 | 17166.000000 | 38089.000000 | 23456.000000 |
| 3 | Все для цветов | 30308.000000 | 19777.000000 | 11551.000000 | 23833.000000 | 38669.000000 | 43285.000000 | 95417.000000 | 106029.000000 | 37308.000000 | 29282.000000 | 24726.000000 | 19411.000000 | 21243.000000 |
| 4 | Другое | 16156.000000 | 6239.000000 | 10942.000000 | 8350.000000 | 7358.000000 | 8450.000000 | 5903.000000 | 2910.000000 | 2762.000000 | 2278.000000 | 3397.000000 | 7895.000000 | 5479.000000 |
| 5 | Товары для хранения вещей | 6479.000000 | 10889.000000 | 7635.000000 | 10529.000000 | 11882.000000 | 14318.000000 | 10773.000000 | 7723.000000 | 56205.000000 | 6339.000000 | 15483.000000 | 17523.000000 | 16225.000000 |
| 6 | Хозтовары и инвентарь | 123225.000000 | 146471.000000 | 138623.000000 | 40300.000000 | 99131.000000 | 79168.000000 | 130308.000000 | 76623.000000 | 48795.000000 | 76044.000000 | 84645.000000 | 48507.000000 | 90946.000000 |
1. Проанализируем категорию "Все для цветов"
# Создадим таблицу
flowers_tab = data.query('categories_product == "Все для цветов"')
# Сгруппируем данные по наименованию товара и месяцу
tab_1 = flowers_tab.pivot_table(index=['first_name', 'month'], values='price', aggfunc='median')\
.sort_values(by='price',ascending=False)\
.reset_index()
#.rename(columns={'first_name':'Источник','acquisition_cost': 'CAC'})\
#.round(2)
tab_1[:10]
| first_name | month | price | |
|---|---|---|---|
| 0 | гортензия | 2019-09-01 | 3599.0 |
| 1 | афеляндра | 2018-10-01 | 3524.0 |
| 2 | цитрофортунелла | 2019-04-01 | 3074.0 |
| 3 | эвкалипт | 2019-06-01 | 1762.0 |
| 4 | эвкалипт | 2019-03-01 | 1762.0 |
| 5 | эвкалипт | 2019-02-01 | 1409.0 |
| 6 | эвкалипт | 2018-10-01 | 1409.0 |
| 7 | скиммия | 2018-12-01 | 1139.0 |
| 8 | пахира | 2019-10-01 | 1087.0 |
| 9 | мединилла | 2019-09-01 | 1034.0 |
Посчитаем частоту покупок
# Сгруппируем данные по наименованию и посчитаем частоту покупок по каждой позиции
flowers_freq = tab_1.pivot_table(index=['first_name'], values='month', aggfunc='count')\
.reset_index()
# Выделим топ-10 самых продаваемых позиций в теч года
flowers_freq_year = flowers_freq.query('month >8').sort_values(by='month',ascending=False).head(10)
flowers_freq_year
| first_name | month | |
|---|---|---|
| 20 | герань | 13 |
| 67 | пеларгония | 13 |
| 74 | рассада | 12 |
| 75 | роза | 12 |
| 30 | искусственный | 11 |
| 97 | цветок | 11 |
| 58 | мята | 9 |
| 91 | фиалка | 9 |
**Наблюдение:**
За основной товар в категории "Все для цветов" можно взять товар, который продавался каждый месяц.
2. Проанализируем категорию "Все для ванной"
# Создадим таблицу
bathroom_tab = data.query('categories_product == "Все для ванной"')
# Сгруппируем данные по наименованию товара и месяцу
tab_2 = bathroom_tab.groupby('first_name').agg({'order_id' : 'count', 'price':'median'})\
.sort_values(by='order_id',ascending=False)\
.reset_index()
tab_2
| first_name | order_id | price | |
|---|---|---|---|
| 0 | сушилка | 267 | 637.0 |
| 1 | штора | 76 | 974.0 |
| 2 | корзина | 33 | 592.0 |
| 3 | ёрш | 15 | 56.0 |
| 4 | контейнер | 15 | 674.0 |
| 5 | карниз | 14 | 224.0 |
| 6 | сиденье | 8 | 374.0 |
| 7 | штанга | 8 | 974.0 |
| 8 | комплект | 7 | 178.0 |
| 9 | стакан | 6 | 217.0 |
| 10 | настенная | 5 | 824.0 |
| 11 | полотенце | 4 | 89.0 |
| 12 | махровое | 4 | 130.5 |
| 13 | подголовник | 3 | 322.0 |
| 14 | ковш | 3 | 64.0 |
| 15 | пробка | 2 | 66.5 |
| 16 | сетка | 2 | 258.5 |
| 17 | ерш | 2 | 2661.5 |
| 18 | махровый | 1 | 1949.0 |
| 19 | ванна | 1 | 749.0 |
| 20 | ковры | 1 | 599.0 |
| 21 | фен | 1 | 592.0 |
| 22 | халат | 1 | 1949.0 |
| 23 | вешалка-сушилка | 1 | 1087.0 |
| 24 | бак | 1 | 3749.0 |
**Наблюдение:**
Распределить категорию "Все для ванной" можно следующим образом:
Основной:
Дополнительный:
3. Проанализируем категорию "Все для кухни"
# Создадим таблицу
kitchen_tab = data.query('categories_product == "Все для кухни"')
# Сгруппируем данные по наименованию товара и месяцу
tab_3 = kitchen_tab.groupby('first_name').agg({'order_id' : 'count', 'price':'median'})\
.sort_values(by='order_id',ascending=False)\
.reset_index()
tab_3
| first_name | order_id | price | |
|---|---|---|---|
| 0 | тарелка | 39 | 97.0 |
| 1 | набор | 36 | 405.0 |
| 2 | салатник | 22 | 104.5 |
| 3 | чайник | 19 | 749.0 |
| 4 | банка | 17 | 164.0 |
| 5 | нож | 14 | 123.0 |
| 6 | кружка | 14 | 89.0 |
| 7 | сковорода | 9 | 712.0 |
| 8 | ложка | 9 | 142.0 |
| 9 | термокружка | 8 | 524.0 |
| 10 | кувшин | 8 | 149.0 |
| 11 | вилка | 7 | 187.0 |
| 12 | кастрюля | 6 | 1368.0 |
| 13 | весы | 6 | 1274.0 |
| 14 | термос | 5 | 554.0 |
| 15 | чайный | 5 | 802.0 |
| 16 | миска | 4 | 44.0 |
| 17 | терка | 4 | 246.5 |
| 18 | овсянница | 4 | 97.0 |
| 19 | хлебница | 4 | 693.0 |
| 20 | tepмокружка | 4 | 2361.5 |
| 21 | измельчитель | 4 | 479.0 |
| 22 | лоток | 4 | 471.5 |
| 23 | емкость | 3 | 265.0 |
| 24 | разделочная | 3 | 194.0 |
| 25 | овощеварка | 3 | 449.0 |
| 26 | чайная | 3 | 172.0 |
| 27 | столовый | 2 | 193.5 |
| 28 | соковарка | 2 | 1686.5 |
| 29 | кисточка | 2 | 59.0 |
| 30 | термостакан | 2 | 295.5 |
| 31 | толкушка | 2 | 651.5 |
| 32 | сахарница | 2 | 172.0 |
| 33 | рыбочистка | 2 | 97.0 |
| 34 | сервировочная | 2 | 28.0 |
| 35 | блюдце | 2 | 56.5 |
| 36 | крышка | 2 | 82.0 |
| 37 | блюдо | 2 | 262.0 |
| 38 | кухонное | 2 | 73.5 |
| 39 | лопатка | 2 | 55.5 |
| 40 | мусорный | 2 | 4030.5 |
| 41 | бидон | 2 | 138.0 |
| 42 | миксер | 2 | 801.5 |
| 43 | просеиватель | 2 | 202.0 |
| 44 | венчик | 1 | 67.0 |
| 45 | тортница | 1 | 824.0 |
| 46 | бульонница | 1 | 164.0 |
| 47 | мантоварка | 1 | 1349.0 |
| 48 | сотейник | 1 | 1162.0 |
| 49 | скалка | 1 | 1312.0 |
| 50 | мантоварка-пароварка | 1 | 2219.0 |
| 51 | котел | 1 | 2924.0 |
| 52 | половник | 1 | 83.0 |
| 53 | отделитель | 1 | 1649.0 |
| 54 | овощечистка | 1 | 749.0 |
| 55 | модульная | 1 | 825.0 |
| 56 | электроштопор | 1 | 1012.0 |
**Наблюдение:**
Распределить категорию "Все для кухни" можно следующим образом:
Основной:
Дополнительный:
4. Проанализируем категорию "Товары для хранения вещей"
# Создадим таблицу
storage_tab = data.query('categories_product == "Товары для хранения вещей"')
# Сгруппируем данные по наименованию товара и месяцу
tab_4 = storage_tab.groupby('first_name').agg({'order_id' : 'count', 'price':'median'})\
.sort_values(by='order_id',ascending=False)\
.reset_index()
tab_4
| first_name | order_id | price | |
|---|---|---|---|
| 0 | вешалка | 36 | 142.0 |
| 1 | полки | 21 | 2249.0 |
| 2 | сумка | 13 | 374.0 |
| 3 | подставка | 6 | 366.5 |
| 4 | вешалка-плечики | 5 | 45.0 |
| 5 | плечики | 5 | 22.0 |
| 6 | вешалки | 4 | 164.0 |
| 7 | комод | 4 | 1349.0 |
| 8 | этажерка | 4 | 1027.0 |
| 9 | вешалка-стойка | 3 | 1837.0 |
| 10 | стеллаж | 3 | 1087.0 |
| 11 | полка | 2 | 1799.0 |
| 12 | обувница-3 | 1 | 1912.0 |
| 13 | складной | 1 | 1424.0 |
**Наблюдение:**
Распределить категорию "Товары для хранения вещей" можно следующим образом:
Основной:
Дополнительный:
5. Проанализируем категорию "Хозтовары и инвентарь"
# Создадим таблицу
household_tab = data.query('categories_product == "Хозтовары и инвентарь"')
# Сгруппируем данные по наименованию товара и месяцу
tab_5 = household_tab.groupby('first_name').agg({'order_id' : 'count', 'price':'median'})\
.sort_values(by='order_id',ascending=False)\
.reset_index()
tab_5
| first_name | order_id | price | |
|---|---|---|---|
| 0 | сумка-тележка | 233 | 1649.0 |
| 1 | муляж | 166 | 59.0 |
| 2 | тележка | 96 | 734.0 |
| 3 | таз | 68 | 239.0 |
| 4 | стремянка | 28 | 1949.0 |
| 5 | щетка | 15 | 487.0 |
| 6 | швабра | 13 | 899.0 |
| 7 | салфетка | 12 | 196.5 |
| 8 | новогоднее | 12 | 1049.0 |
| 9 | ведро | 11 | 149.0 |
| 10 | лестница-стремянка | 11 | 1574.0 |
| 11 | перчатки | 9 | 224.0 |
| 12 | щетка-сметка | 8 | 61.5 |
| 13 | окномойка | 8 | 412.0 |
| 14 | крючок | 7 | 38.0 |
| 15 | корыто | 7 | 749.0 |
| 16 | подарочный | 6 | 194.0 |
| 17 | камнеломка | 5 | 97.0 |
| 18 | стяжка | 5 | 20.0 |
| 19 | стремянки | 5 | 3712.0 |
| 20 | насадка | 5 | 262.0 |
| 21 | жестяная | 5 | 104.0 |
| 22 | шнур | 5 | 37.0 |
| 23 | петля | 5 | 44.0 |
| 24 | коробка | 4 | 629.0 |
| 25 | зубная | 4 | 164.0 |
| 26 | веник | 4 | 339.5 |
| 27 | термометр | 3 | 44.0 |
| 28 | ткань | 3 | 285.0 |
| 29 | урна | 3 | 6899.0 |
| 30 | совок | 3 | 592.0 |
| 31 | ящик | 3 | 224.0 |
| 32 | дозатор | 3 | 524.0 |
| 33 | крепеж | 3 | 19.0 |
| 34 | паста | 3 | 899.0 |
| 35 | мыло | 3 | 78.0 |
| 36 | вантуз | 2 | 100.5 |
| 37 | урна-пепельница | 2 | 3760.5 |
| 38 | измерительный | 2 | 374.0 |
| 39 | тряпкодержатель | 2 | 119.0 |
| 40 | пылесос | 2 | 2121.5 |
| 41 | шпагат | 2 | 48.0 |
| 42 | мешок | 2 | 63.0 |
| 43 | бензин | 2 | 67.0 |
| 44 | стремянка-табурет | 2 | 2699.0 |
| 45 | бельевые | 2 | 66.5 |
| 46 | ваза | 1 | 488.0 |
| 47 | шило | 1 | 44.0 |
| 48 | щетка-утюжок | 1 | 44.0 |
| 49 | щётка | 1 | 1124.0 |
| 50 | почтовый | 1 | 277.0 |
| 51 | кондиционер | 1 | 262.0 |
| 52 | фал | 1 | 2099.0 |
| 53 | корзинка | 1 | 1049.0 |
| 54 | лестница | 1 | 974.0 |
| 55 | стяжки | 1 | 637.0 |
| 56 | многофункциональный | 1 | 637.0 |
| 57 | петля-стрела | 1 | 52.0 |
| 58 | автоматическая | 1 | 7229.0 |
**Наблюдение:**
Распределить категорию "Хозтовары и инвентарь" можно следующим образом:
Основной:
Дополнительный:
6. Проанализируем категорию "Все для спальни"
# Создадим таблицу
bedroom_tab = data.query('categories_product == "Все для спальни"')
# Сгруппируем данные по наименованию товара и месяцу
tab_6 = bedroom_tab.groupby('first_name').agg({'order_id' : 'count', 'price':'median'})\
.sort_values(by='order_id',ascending=False)\
.reset_index()
tab_6
| first_name | order_id | price | |
|---|---|---|---|
| 0 | гладильная | 118 | 1611.5 |
| 1 | коврик | 104 | 487.5 |
| 2 | чехол | 88 | 299.0 |
| 3 | скатерть | 31 | 974.0 |
| 4 | подкладка | 13 | 127.0 |
| 5 | подрукавник | 11 | 224.0 |
| 6 | ковер | 6 | 1012.0 |
| 7 | кофр | 6 | 531.5 |
| 8 | рукав | 5 | 225.0 |
| 9 | покрывало | 4 | 3261.5 |
| 10 | плед | 4 | 712.0 |
| 11 | покрытие | 4 | 1199.5 |
| 12 | одеяло | 3 | 1725.0 |
| 13 | простыня | 3 | 899.0 |
| 14 | утюг | 2 | 1060.5 |
| 15 | доска | 2 | 1724.0 |
| 16 | подушка | 2 | 239.5 |
| 17 | ковёр | 2 | 749.0 |
| 18 | простынь | 2 | 1852.0 |
| 19 | светильник | 1 | 1199.0 |
| 20 | наматрасник | 1 | 3074.0 |
| 21 | наматрацник | 1 | 1183.0 |
| 22 | двуспальное | 1 | 2024.0 |
| 23 | пододеяльник | 1 | 899.0 |
**Наблюдение:**
Распределить категорию "Все для спальни" можно следующим образом:
Основной:
Дополнительный:
К сезонному товару можно определить только категорию "Все для цветов". Проанализируем ее.
# Сделаем временной срез с апреля по август и группировку по наименованию товара с подчетом заказов
flowers_seas = flowers_tab.query('month == ("2019-04-01","2019-08-01")')\
.groupby('first_name').agg({'order_id' :'count'})\
.reset_index().sort_values(by='order_id', ascending = False)[:10]
flowers_seas
| first_name | order_id | |
|---|---|---|
| 36 | пеларгония | 142 |
| 39 | рассада | 109 |
| 46 | томата | 90 |
| 37 | петуния | 47 |
| 23 | калибрахоа | 27 |
| 4 | бакопа | 15 |
| 20 | искусственный | 15 |
| 34 | однолетнее | 13 |
| 50 | цветок | 8 |
| 40 | роза | 7 |
# Сделаем срез по рассаде и пеларгонии, петунии
rassada = flowers_tab.query('first_name == "рассада"').groupby('month').agg({'order_id' : 'count'}).reset_index()
pel = flowers_tab.query('first_name == "пеларгония"').groupby('month').agg({'order_id' : 'count'}).reset_index()
tomat = flowers_tab.query('first_name == "томата"').groupby('month').agg({'order_id' : 'count'}).reset_index()
petun = flowers_tab.query('first_name == "петуния"').groupby('month').agg({'order_id' : 'count'}).reset_index()
# Построим график
ax =rassada.plot(kind='bar', y = 'order_id', figsize = (15, 5), label='рассада', color='#008080',)
pel.plot(kind='bar', y = 'order_id', figsize = (15, 5), ax=ax, alpha=0.5, color='#da70d6', label='пеларгония')
tomat.plot(kind='bar', y = 'order_id', figsize = (15, 5), ax=ax, alpha=0.5, color='#cf0', label='томата')
petun.plot(kind='bar', y = 'order_id', figsize = (15, 5), ax=ax, alpha=0.5, color='#000080', label='петуния')
plt.xlabel('Месяц')
plt.ylabel('Количество заказов')
plt.title('График количества заказов растений')
ax.legend()
plt.show()
**Наблюдение:**
К сезонному товару можно отнести
Для проверки двух гипотез выберем метод scipy.stats.ttest_ind( гипотеза о равенстве средних двух совокупностей)
Три аспекта, которые должны быть соблюдены:
Перед проверкой гипотезы найдем средний доходза месяц
# Сгруппируем данные по месяцу и категории
data_hypoth = data.groupby(['month','categories_product'])['revenue'].agg(['sum']).reset_index()
data_hypoth
| month | categories_product | sum | |
|---|---|---|---|
| 0 | 2018-10-01 | Все для ванной | 73656.0 |
| 1 | 2018-10-01 | Все для кухни | 34313.0 |
| 2 | 2018-10-01 | Все для спальни | 66237.0 |
| 3 | 2018-10-01 | Все для цветов | 30308.0 |
| 4 | 2018-10-01 | Другое | 16156.0 |
| ... | ... | ... | ... |
| 86 | 2019-10-01 | Все для спальни | 23456.0 |
| 87 | 2019-10-01 | Все для цветов | 21243.0 |
| 88 | 2019-10-01 | Другое | 5479.0 |
| 89 | 2019-10-01 | Товары для хранения вещей | 16225.0 |
| 90 | 2019-10-01 | Хозтовары и инвентарь | 90946.0 |
91 rows × 3 columns
# Сделаем срез по категории "Все для цветов" и "Все для ванной" и найдем среднию
data_hypoth_flowers = data_hypoth.query('categories_product == "Все для цветов"')['sum']
print('Средний доход в месяц по категории "Все для цветов"', round(data_hypoth_flowers.mean(),1))
data_hypoth_bath = data_hypoth.query('categories_product == "Все для ванной"')['sum']
print('Средний доход в месяц по категории "Все для ванной"', round(data_hypoth_bath.mean(), 1))
Средний доход в месяц по категории "Все для цветов" 38526.1 Средний доход в месяц по категории "Все для ванной" 39726.2
После определения среднего дохода приступим к формулировке гипотез
Формулировка гипотез:
Нулевая гипотеза(H_0): data_hypoth_flowers = data_hypoth_bath - Средние доходы категорий "Все для цветов" и "Все для ванной" одинаковыеАльтернативная гипотеза (H_a): data_hypoth_flowers ≠ data_hypoth_bath- Средние доходы категорий "Все для цветов" и "Все для ванной" разныеalpha = 0.05 - выберим данный уровень значимости (вероятный порог "необычности")
data_hypoth_flowers - средний доход за месяц категории "Все для цветов"\ data_hypoth_bath - средний доход за месяц категории "Все для ванной"
from scipy import stats as st
results = st.ttest_ind(
data_hypoth_flowers,
data_hypoth_bath, equal_var = False) # results = вызов метода для проверки гипотезы
alpha = .05 # alpha уровнь значимости
print('p-значение:', results.pvalue) # вывод значения p-value на экран
if results.pvalue < alpha:
print("Отвергаем нулевую гипотезу")
else:
print("Не получилось отвергнуть нулевую гипотезу, средний ежемесячный доход категорий одинаковый")# условный оператор с выводом строки с ответом
p-значение: 0.8985080626295798 Не получилось отвергнуть нулевую гипотезу, средний ежемесячный доход категорий одинаковый
Найдем дисперссию двух генеральных совокупностей
a = np.var(data_hypoth_flowers)
b = np.var(data_hypoth_bath)
print(np.average(a))
print(np.average(b))
778458860.9940829 256028016.2840236
Построим гистограмму распределения среднего дохода категорий "Все для цветов" и "Все для ванной"
# Выведим таблицу в разрезе категорий, периода и дохода
month_category
| categories_product | revenue | |||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| month | NaT | 2018-10-01 | 2018-11-01 | 2018-12-01 | 2019-01-01 | 2019-02-01 | 2019-03-01 | 2019-04-01 | 2019-05-01 | 2019-06-01 | 2019-07-01 | 2019-08-01 | 2019-09-01 | 2019-10-01 |
| 0 | Все для ванной | 73656.0 | 35282.0 | 47196.0 | 37088.0 | 59539.0 | 47284.0 | 22512.0 | 13439.0 | 31700.0 | 52218.0 | 22573.0 | 28983.0 | 44970.0 |
| 1 | Все для кухни | 34313.0 | 69663.0 | 32233.0 | 29623.0 | 41182.0 | 23787.0 | 7217.0 | 9007.0 | 4255.0 | 9889.0 | 12920.0 | 17514.0 | 5287.0 |
| 2 | Все для спальни | 66237.0 | 71451.0 | 93730.0 | 84394.0 | 46685.0 | 33245.0 | 43992.0 | 13083.0 | 52599.0 | 50311.0 | 17166.0 | 38089.0 | 23456.0 |
| 3 | Все для цветов | 30308.0 | 19777.0 | 11551.0 | 23833.0 | 38669.0 | 43285.0 | 95417.0 | 106029.0 | 37308.0 | 29282.0 | 24726.0 | 19411.0 | 21243.0 |
| 4 | Другое | 16156.0 | 6239.0 | 10942.0 | 8350.0 | 7358.0 | 8450.0 | 5903.0 | 2910.0 | 2762.0 | 2278.0 | 3397.0 | 7895.0 | 5479.0 |
| 5 | Товары для хранения вещей | 6479.0 | 10889.0 | 7635.0 | 10529.0 | 11882.0 | 14318.0 | 10773.0 | 7723.0 | 56205.0 | 6339.0 | 15483.0 | 17523.0 | 16225.0 |
| 6 | Хозтовары и инвентарь | 123225.0 | 146471.0 | 138623.0 | 40300.0 | 99131.0 | 79168.0 | 130308.0 | 76623.0 | 48795.0 | 76044.0 | 84645.0 | 48507.0 | 90946.0 |
# Построим гостограмму по среднему ежемесячному доходу двух категорий
figure(figsize=(10, 5), dpi=100)
data.query('categories_product == ["Все для цветов","Все для ванной"] & revenue> 1000')\
.groupby('categories_product')['revenue']\
.plot(kind='hist', bins=30, alpha=0.5)
plt.legend(["Все для цветов","Все для ванной"])
plt.xlabel('Выручка')
plt.ylabel('Кол-во')
plt.show()
Наблюдение: Вероятность p-value высока и равна 91%, что говорит о том, что нулевая гипотеза верна и значима. Кроме этого, данные математического расчета также это подтвердили. На гистограмме видно, многочисленное совпадение оценки пользователей двух категорий.
Перед проверкой гипотезы найдем среднюю чек покупателя
# Сгруппируем данные по покупателю и категории
data_hypoth_user = data.groupby(['customer_id','categories_product'])['revenue'].agg(['sum']).reset_index()
data_hypoth_user
| customer_id | categories_product | sum | |
|---|---|---|---|
| 0 | 000d6849-084e-4d9f-ac03-37174eaf60c4 | Все для цветов | 555.0 |
| 1 | 001cee7f-0b29-4716-b202-0042213ab038 | Все для ванной | 442.0 |
| 2 | 00299f34-5385-4d13-9aea-c80b81658e1b | Хозтовары и инвентарь | 914.0 |
| 3 | 002d4d3a-4a59-406b-86ec-c3314357e498 | Хозтовары и инвентарь | 1649.0 |
| 4 | 003bbd39-0000-41ff-b7f9-2ddaec152037 | Товары для хранения вещей | 2324.0 |
| ... | ... | ... | ... |
| 2541 | ff601403-b094-4b86-9ac6-264d725b9277 | Хозтовары и инвентарь | 1649.0 |
| 2542 | ffaeab76-3a8d-49ee-860f-17273b2fc8a2 | Хозтовары и инвентарь | 397.0 |
| 2543 | ffb5976a-7a4d-460b-95c4-5ffaba31cb24 | Хозтовары и инвентарь | 389.0 |
| 2544 | ffb80538-3fda-4351-8ea9-9d2bec58bb07 | Все для ванной | 974.0 |
| 2545 | ffe82299-3f5b-4214-87fe-3d36ecccfac3 | Все для ванной | 577.0 |
2546 rows × 3 columns
# Сделаем срез по категории "Все для цветов" и "Все для ванной" и найдем среднию
data_hypoth_flowers_user = data_hypoth_user.query('categories_product == "Все для цветов"')['sum']
print('Средний чек покупателя в месяц по категории "Все для цветов"', round(data_hypoth_flowers_user.mean(),1))
data_hypoth_bath_user = data_hypoth_user.query('categories_product == "Все для ванной"')['sum']
print('Средний чек покупателя по категории "Все для ванной"', round(data_hypoth_bath_user.mean(), 1))
Средний чек покупателя в месяц по категории "Все для цветов" 742.0 Средний чек покупателя по категории "Все для ванной" 1284.7
После определения среднего чека приступим к формулировке гипотез
Формулировка гипотез:
Нулевая гипотеза(H_0): ddata_hypoth_flowers_user = data_hypoth_bath_user - Средний чек на покупателя категорий "Все для цветов" и "Все для ванной" одинаковыеАльтернативная гипотеза (H_a): data_hypoth_flowers_user ≠ data_hypoth_bath_user- Средний чек на покупателя категорий "Все для цветов" и "Все для ванной" разныеalpha = 0.05 - выберим данный уровень значимости (вероятный порог "необычности")
data_hypoth_flowers_user - Средний чек на покупателя категории "Все для цветов"\ data_hypoth_bath_user - Средний чек на покупателя категории "Все для ванной"
from scipy import stats as st
results = st.ttest_ind(
data_hypoth_flowers_user,
data_hypoth_bath_user, equal_var = False) # results = вызов метода для проверки гипотезы
alpha = .05 # alpha уровнь значимости
print('p-значение:', results.pvalue) # вывод значения p-value на экран
if results.pvalue < alpha:
print("Отвергаем нулевую гипотезу, средние чеки на одного покупателя разные")
else:
print("Не получилось отвергнуть нулевую гипотезу, средний чек на одгого покупателя двух категорий одинаковый")# условный оператор с выводом строки с ответом
p-значение: 0.0001773284239909108 Отвергаем нулевую гипотезу, средние чеки на одного покупателя разные
Найдем дисперссию двух генеральных совокупностей
a = np.var(data_hypoth_flowers_user)
b = np.var(data_hypoth_bath_user)
print(np.average(a))
print(np.average(b))
1257982.8160307251 7522341.502388555
Построим гистограмму распределения среднего чека на одного покупателя категорий "Все для цветов" и "Все для ванной"
figure(figsize=(10, 5), dpi=100)
data.query('categories_product == ["Все для цветов","Все для ванной"] & revenue> 2000').groupby('categories_product')['revenue']\
.plot(kind='hist', bins=30, alpha=0.5)
plt.legend(["Все для цветов","Все для ванной"])
plt.xlabel('Выручка')
plt.ylabel('Кол-во')
plt.show()
**Наблюдение:** Получив крайне маленькое значение p-value, мы отвергли Нулевую гипотезу. Таким образом, у нас практически нет вероятности получить одинаково средние чеки на одного покупателя. Перепроверка математическим путем и визуализация гистограммы нам это тоже показала.
ВЫВОД:
Подготовим презентацию исследования для заказчика.
Презентация: ссылка на облачное хранилище с презентацией
Просмотр дашборта: ссылка